package com.yugao.fintech.antelope.infra.weixin.mp.controller;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.yugao.fintech.antelope.base.config.core.ConfigFactory;
import com.yugao.fintech.antelope.infra.common.InfraCons;
import com.yugao.fintech.antelope.infra.weixin.mp.MpContextHolder;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.WxMpHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.model.WxMpContext;
import com.yugao.fintech.antelope.infra.weixin.mp.model.rqrs.MpOpenHandleMessageReq;
import com.yugao.fintech.antelope.base.log.OperateLog;
import com.yugao.fintech.antelope.base.model.enums.ConfigDefineEnum;
import com.yugao.fintech.antelope.base.model.enums.ConfigKeyEnum;
import com.yugao.fintech.antelope.base.model.module.config.ConfigInfo;
import com.yugao.fintech.antelope.base.model.module.config.param.BaseConfigParam;
import com.yugao.fintech.antelope.base.model.module.config.param.WxMpParam;
import com.yugao.fintech.antelope.base.tenant.core.utils.TenantUtils;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.context.annotation.Lazy;

import java.util.Objects;


@Slf4j
@Api(tags = "微信公共部分")
@RestController
@RequestMapping("/wxMpOpen")
public class MpOpenController {
    @Autowired
    @Lazy
    private WxMpHandler wxMpHandler;
    @Autowired
    private ConfigFactory configFactory;

    /**
     * 接收微信公众号的校验签名
     * <a href="https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index">地址</a>
     * 配置接口后点击提交, 微信验证地址是否合法会向配置的地址发送一个get请求
     * @param tenantId 租户id
     * @see InfraCons#URL_WX_MP_PUSH
     */
    @OperateLog(isSaveDb = false)
    @GetMapping(value = "/push/{tenantId}")
    public String checkSignature(@RequestParam("signature") String signature,
                              @RequestParam("timestamp") String timestamp,
                              @RequestParam("nonce") String nonce,
                              @PathVariable("tenantId") Long tenantId,
                              @RequestParam("echostr") String echoStr) {
        return TenantUtils.execute(tenantId, () -> {
            ConfigInfo configInfo = configFactory.getConfig(ConfigDefineEnum.WX_MP);
            if (Objects.isNull(configInfo)) {
                log.warn("尚未配置或者使能微信参数");
                return "fail";
            }
            // 校验签名
            WxMpParam param = BaseConfigParam.jsonToObject(ConfigKeyEnum.WX_MP, configInfo.getConfigValue());
            me.chanjar.weixin.mp.api.WxMpService service = wxMpHandler.getContextAndAutoCreate(param).getService();
            if (service.checkSignature(timestamp, nonce, signature)){
                // 校验成功原样返回 echoStr
                return echoStr;
            }
            // 校验失败
            return "fail";
        });
    }


    /**
     * 接收微信公众号的消息推送
     *
     * <a href="https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html">文档</a>
     * @see InfraCons#URL_WX_MP_PUSH
     */
    @OperateLog(isSaveDb = false)
    @PostMapping(value = "/push/{tenantId}",  produces = "application/xml; charset=UTF-8")
    public String handleMessage(@PathVariable("tenantId") Long tenantId,
                                @RequestBody String content,
                                MpOpenHandleMessageReq req) {

        return TenantUtils.execute(tenantId, () -> {
            log.info("[handleMessage][tenantId({}) 推送消息，参数({}) 内容({})]", tenantId, req, content);
            try {
                ConfigInfo config = configFactory.getConfig(ConfigDefineEnum.WX_MP);
                if (Objects.isNull(config)) {
                    log.warn("尚未配置或者使能微信参数");
                    return "fail";
                }

                WxMpParam param = BaseConfigParam.jsonToObject(ConfigKeyEnum.WX_MP, config.getConfigValue());
                MpContextHolder.setParam(param);
                return handleMessage0(content, req);
            } finally {
                MpContextHolder.clear();
            }
        });
    }

    private String handleMessage0(String content, MpOpenHandleMessageReq req) {
        WxMpParam param = MpContextHolder.getParam();
        WxMpContext context = wxMpHandler.getContextAndAutoCreate(param);
        // 校验请求签名
        me.chanjar.weixin.mp.api.WxMpService mppService = context.getService();
        Assert.isTrue(mppService.checkSignature(req.getTimestamp(), req.getNonce(), req.getSignature()),
                "非法请求");

        // 第一步，解析消息
        WxMpXmlMessage inMessage = null;
        if (StrUtil.isBlank(req.getEncrypt_type())) { // 明文模式
            inMessage = WxMpXmlMessage.fromXml(content);
        } else if (Objects.equals(req.getEncrypt_type(), MpOpenHandleMessageReq.ENCRYPT_TYPE_AES)) { // AES 加密模式
            inMessage = WxMpXmlMessage.fromEncryptedXml(content, mppService.getWxMpConfigStorage(),
                    req.getTimestamp(), req.getNonce(), req.getMsg_signature());
        }
        Assert.notNull(inMessage, "消息解析失败，原因：消息为空");

        // 第二步，处理消息
        WxMpMessageRouter mpMessageRouter = context.getRouter();
        WxMpXmlOutMessage outMessage = mpMessageRouter.route(inMessage);
        if (outMessage == null) {
            return "";
        }

        // 第三步，返回消息
        if (StrUtil.isBlank(req.getEncrypt_type())) { // 明文模式
            return outMessage.toXml();
        } else if (Objects.equals(req.getEncrypt_type(), MpOpenHandleMessageReq.ENCRYPT_TYPE_AES)) { // AES 加密模式
            return outMessage.toEncryptedXml(mppService.getWxMpConfigStorage());
        }
        return "";
    }
}
